各位好~雖然今天是挑戰的最後一天但是筆者還是會把這個系列介紹完,下面是目前的進度。
Struct
另外是今天有讀者問我還會不會繼續寫 wasm ,雖然那是我當初訂的題目沒錯但是後來幾乎都在寫 Rust (慚愧)剛開始的時候想得太美好以為時間很充裕沒想到轉眼已經 30 天了 XD。
雖然這次挑戰 wasm 寫得很少但是我之後會找個地方繼續寫 wasm 相關的文章,有興趣的同學可以先繼續關注這邊。
以下正文開始
還記得上一篇我們當我們需要呼叫函式時的寫法嗎?
pub fn eat_at_restaurant() {
// Absolute path
crate::front_of_house::hosting::add_to_waitlist();
// Relative path
front_of_house::hosting::add_to_waitlist();
}
不管是用絕對路徑或是相對路徑都必須寫的很長一串,所幸 Rust 提供 use
的方法來引入物件至當前的 scope 例如下面這隻程式就把 hosting
的 module 給全部引入至 scope 裡面
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
hosting::add_to_waitlist();
hosting::add_to_waitlist();
}
由這支程式可以看到當我們 use
hosting
之後 eat_at_restaurant
就可以使用 hosting
了。
至於若是 use
的路徑要使用相對路徑則比較特別我們必須在前面加上 self
,例如下面的範例,
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use self::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
hosting::add_to_waitlist();
hosting::add_to_waitlist();
}
不過根據官網的說明這樣的寫法似乎只是暫時的 Rust 團隊說未來會把它消除掉,所以就請各位隨時注意更新的情況囉。
use
Paths接著來討論一下慣用的用法,通常在 use
的時候我們不會這樣寫,
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting::add_to_waitlist;
pub fn eat_at_restaurant() {
add_to_waitlist();
add_to_waitlist();
add_to_waitlist();
}
這樣我們通常會搞不清楚到底 add_to_waitlist
是不是屬於這個 scope 的函式若是我們保留他的父層 hosting
就像我們在上一個例子做的一樣,在判讀程式碼的時候會更直覺的知道他是來至不同 scope 的程式,因此要維持良好的寫程式習慣的話請不要這樣做囉。
不過若是在 use
像是 structs
、enums
等其他物件的時候我們通常就會使用完整的路徑,例如
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(1, 2);
}
這個例子直接指定路徑到 HashMap
並且調用。
不過官網也說了這邊倒是沒有什麼特別的理由就純粹是慣例方便閱讀,不過有時候還是會有例外,舉例來說當你有兩個同樣名稱的物件的時候,
use std::fmt;
use std::io;
fn function1() -> fmt::Result {
// --snip--
}
fn function2() -> io::Result<()> {
// --snip--
}
像是這邊就必須得用父層來區分。
Note: 慣例就只是建議並不代表你一定要照著做,若是團隊有共識要用什麼風格撰寫沒人管你。
使用 as 賦予一個新的名稱,這邊相信有寫過其他語言的大概都不陌生所以我們直接來看例子,
use std::fmt::Result;
use std::io::Result as IoResult;
fn function1() -> Result {
// --snip--
}
fn function2() -> IoResult<()> {
// --snip--
}
這邊可以看到我們用 as 把 Result
重新命名成 IoResult
,這樣就可以避免重複名稱的問題囉。
pub use
用 pub
和 use
重新輸出物件,這邊其實也就是可以更加偷懶的先把想要 public 的路徑先寫好其他程式在 use
時就不用再寫很長一串,例如下面的寫法。
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
hosting::add_to_waitlist();
hosting::add_to_waitlist();
}
這樣就等於把 hosting
給直接 public 出去了。
而這樣寫的好處是可以把 pub
跟 use
的路徑結構分開例如當 hosting
在這支程式的路徑變了我只要透過修改 pub use
後面的路徑即可這樣其他有用到 hosting
的程式都不會被影響,很好的分離了實作跟 api 介面。
通常當我們新增依賴也就是 crate 的時候該 package 的名字就是我們用來 use
的根目錄,還記得很久以前我們用過的 rand
嗎?
[dependencies]
rand = "0.5.5"
只要引入之後我們就可以在任何 scope 裡面取用到,
use rand::Rng;
fn main() {
let secret_number = rand::thread_rng().gen_range(1, 101);
}
同樣的 Rust 裡面也有內建一些 package 像是 std
,
use std::collections::HashMap;
而因為是內建的所以我們不必把他加進依賴就可以直接使用。
另外就是當我們需要從同個 module 或是 package 取用多個物件的時候我們可以用 {}
的方式拿到我們需要的,例如這支程式
use std::io;
use std::cmp::Ordering;
我們可以改寫成這樣,
use std::{cmp::Ordering, io};
這樣我們就可以減少很多行的 use
並且讓程式更好讀。
我們可以用這樣的方式整理任何在同個路徑下的檔案,例如這支程式
use std::io;
use std::io::Write;
可以改寫成這樣,
use std::io::{self, Write};
最後若是非常懶用到的物件很多我們可以直接用 *
把他們全部引入,例如這支程式
use std::collections::*;
這樣就等於把 collections
下面全部的物件都引入這個 scope 裡面了。
不過官網說不建議這樣做原因是這樣開發者就不知道到底有用到哪些物件了,通常會這樣做是在寫測試的時候,如果未來有機會的話再介紹,有興趣的可以先自己看看 How to Write Tests。
透過這幾篇所教的 module、path 和 use 我們就可以把程式碼拆開來放在各個不同的檔案下面,這邊我就不多做介紹了留給讀者自己去試試看吧!
那麼今天就結束了,我們明天見!
ch07-04-bringing-paths-into-scope-with-the-use-keyword
在找一些資料時意外看到你的文章,因為我自己也很喜歡 Rust ,也寫了一個系列,所以就看完了,雖然最後還是因為沒有 wasm 而覺得可惜,對了,抱歉突然留言轟炸,因為這幾天登入出問題導致我沒辦法留言,就先把想留言的部份記下來,等系統回復再一次留了